package com.futao.niudong.controller;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.*;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author futao@gmail.com
 * @date 2022/3/10
 */
@Slf4j
@RequestMapping("/es/search")
@RestController
public class EsSearchController {

    private static final String SPOT = "spot";

    @Autowired
    private RestHighLevelClient restHighLevelClient;


    /**
     * 建议
     *
     * @param cityName
     * @return
     * @throws IOException
     */
    @GetMapping("/suggest")
    public Object suggest(@RequestParam("cityName") String cityName) throws IOException {
        SearchRequest searchRequest = new SearchRequest(SPOT);
        searchRequest.types(SPOT);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        TermSuggestionBuilder suggestion = SuggestBuilders.termSuggestion("city")
                .text(cityName);
        suggestBuilder.addSuggestion("suggest_city", suggestion);
        sourceBuilder.suggest(suggestBuilder);
        searchRequest.source(sourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return JSON.toJSON(search);
    }


    @GetMapping
    public Object search() throws IOException {
        SearchRequest searchRequest = new SearchRequest(SPOT);
        searchRequest.types(SPOT);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 指定要查询的字段和不查询的字段
        sourceBuilder.fetchSource(new String[]{"id", "gxtAge", "city"}, new String[]{});

        // 全部匹配查询
        sourceBuilder.query(QueryBuilders.matchAllQuery()).query(QueryBuilders.termQuery("city", "南昌"));

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field(new HighlightBuilder.Field("city"));
        highlightBuilder.field(new HighlightBuilder.Field("gxtAge"));
        highlightBuilder.highlighterType("unified");
        highlightBuilder.tagsSchema("default");
        sourceBuilder.highlighter(highlightBuilder);

        // 分页开始
        sourceBuilder.from(0);
        // 分页大小
        sourceBuilder.size(50);

        // 默认按照分数值降序排列
        // sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
        // 根据具体字段排序
        sourceBuilder.sort(new FieldSortBuilder("gxtAge").order(SortOrder.DESC));

        // 搜索超时时间
        sourceBuilder.timeout(new TimeValue(2, TimeUnit.SECONDS));
        // 启用分析
        sourceBuilder.profile(true);
        // 搜索条件
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return JSON.toJSON(searchResponse);
    }


    @GetMapping("/scroll/{city}")
    public void scroll(@PathVariable("city") String city) throws IOException {

        SearchRequest searchRequest = new SearchRequest(SPOT);
        searchRequest.types(SPOT);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery("city", city));
        // 每次读取条数
        sourceBuilder.size(10);
        searchRequest.source(sourceBuilder);
        // 设置滚动间隔
        searchRequest.scroll(TimeValue.timeValueMinutes(1L));
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 类似于游标，记录上一次遍历的位置(滚动id每次返回的都是一样的？)
        String scrollId = searchResponse.getScrollId();
        SearchHits hits = searchResponse.getHits();
        log.info("第一次检索的scrollId:{},length:{}，hits:{}", scrollId, hits.getHits().length, JSON.toJSONString(hits, false));
        while (hits != null && hits.getHits().length >= sourceBuilder.size()) {
            SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
            searchScrollRequest.scroll(TimeValue.timeValueSeconds(30L));
            SearchResponse scrollResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
            scrollId = scrollResponse.getScrollId();
            hits = scrollResponse.getHits();
            log.info("检索的scrollId:{}，hits.length:{},hits:{}", scrollId, hits.getHits().length, JSON.toJSONString(hits, false));
        }

        log.info("结束");
    }


    /**
     * 整合多个查询结果
     *
     * @throws IOException
     */
    @GetMapping("/multi-request")
    public void multiSearch() throws IOException {
        MultiSearchRequest multiSearchRequest = new MultiSearchRequest();
        // 添加多个搜索
        SearchRequest request1 = new SearchRequest(SPOT);
        request1.types(SPOT);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.termQuery("city", "南昌"));
        sourceBuilder.from(0);
        sourceBuilder.size(5);
        request1.source(sourceBuilder);
        multiSearchRequest.add(request1);


        // 第二个搜索结果
        SearchRequest request2 = new SearchRequest(SPOT);
        request2.types(SPOT);
        SearchSourceBuilder sourceBuilder2 = new SearchSourceBuilder();
        sourceBuilder2.query(QueryBuilders.termQuery("city", "无锡"));
        sourceBuilder2.from(0);
        sourceBuilder.size(5);
        request2.source(sourceBuilder2);
        multiSearchRequest.add(request2);

        MultiSearchResponse multiSearchResponse = restHighLevelClient.msearch(multiSearchRequest, RequestOptions.DEFAULT);
        MultiSearchResponse.Item[] responses = multiSearchResponse.getResponses();
        for (MultiSearchResponse.Item respons : responses) {
            SearchHits hits = respons.getResponse().getHits();
            System.out.println(JSON.toJSONString(hits, false));
        }
    }

}
